package cmp.compile;
/**********************************************
 * Best Recipocal Hit
 */
import java.sql.ResultSet;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Vector;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jpave.annotator.CoreMain;
import util.Debug.ErrorReport;

import cmp.main.Globals;
import assem.DBConn;
import cmp.compile.panels.CompilePanel;

public class MethodBestRecip {
	 String groupFile = Globals.Compile.GROUP_FILE_BEST_RECIP;
	
	 public boolean run(int idx, DBConn db, CompilePanel panel) {
		String comment = panel.getMethodComment(idx);
		prefix = panel.getMethodPrefix(idx); // Groups should be prefixed with this
		String [] settings = panel.getMethodSettings(idx).split(":");

		overlapCutoff = Double.parseDouble(settings[1]);
		similarity = Double.parseDouble(settings[2]);
		cmpDBC = db;
		cmpPanel = panel;
		blastFile = cmpPanel.getBlastPanel().getBlastFile();
		if (blastFile == null) return false;
		groupFile = cmpPanel.getCurrentProjectDirectory() + "/" + groupFile + "." + prefix;
		
		CompileMain.PrtDateMsg("\nStart execution of Best Recipocal Hit");
		CompileMain.PrtSpMsg(1, "Prefix:     " + prefix + "  Comment: " + comment);
		CompileMain.PrtSpMsg(1, "Overlap:    " + overlapCutoff + "  Similarity: " + similarity);
		CompileMain.PrtSpMsg(1, "Blast File: " + blastFile);		
		long startTime = CompileMain.getTime();
		
		doSteps();
		
		// the following will load from file <project>/groupFile

		if (! new MethodUserDef().run(idx, groupFile, cmpDBC, cmpPanel)) return false;
		
		CompileMain.PrtDateMsgTime("Finish execution of Best Recipocal Hit", startTime);
		return true;
	}
	
	 private boolean doSteps() {
		long t = CompileMain.getTime();
		CompileMain.PrtSpMsg(1, "Do transitive steps");
		
		try {
			/// first step
			
			
			CompileMain.PrtSpMsgTime(1, "Finish first step", t);
			return true;
		} 
		catch (Exception e) {
			ErrorReport.reportError(e, "Error on doSteps"); // writes msg to stdout and e to Cpave.error.log
			return false;
		}
	}
	
	 public boolean makeCutomPOGs() {
		CompileMain.PrtSpMsg(1, "Creating fnPOGs");
		
		if (!removeTmpTables()) return false;
		if (!createTmpTables()) return false;
		if (!readBlast()) return false;
		if (!findPOGs()) return false;
		return true;
		// new LoadPOGs with this file
	}
	
	// reads hits into ztmp_hits
	 private boolean readBlast() 
	{  	
		BufferedReader reader = null;
		String strLine = null;
		String fileBlast = cmpPanel.getBlastPanel().getBlastFile();
		//Added 10/19/12 MW
		//If blast file does not exist, and user hits "cancel" for generation, null is returned
		if(fileBlast == null)
			return false;
		Vector <String> pairs = new Vector <String> ();
		HashMap <String, String> utIdLen = new  HashMap <String, String> ();
		
		try {
			String sql = "SELECT UTstr, UTid, length from unitrans";
			
			ResultSet rs = cmpDBC.executeQuery(sql);
			while (rs.next()) {
				String utstr = rs.getString(1);
				int utid = rs.getInt(2);
				int len = rs.getInt(3)/3;
				utIdLen.put(utstr, utid + ":" + len);
			}
			rs.close();
		}
		catch (Exception e) {
			ErrorReport.die(e, "reading database for unitran lengths");
		}
		CompileMain.PrtSpMsg(1, "Read " + utIdLen.size() + " from database");
		try {
			CompileMain.PrtSpMsg(1, "Loading blast file " + fileBlast);
			reader = new BufferedReader(new FileReader(fileBlast));
			int cntLoad=0, cntRead = 0, cntMinSim=0, cntMinOlap=0;

			while ((strLine = reader.readLine()) != null) 
			{
				if (strLine.length() == 0 || strLine.charAt(0) == '#') continue; 

				cntRead++;
				String tokens[] = strLine.split("\t");
				String ut1str = getID(tokens[0].trim());
				String ut2str = getID(tokens[1].trim());
				
				if (ut1str.equals(ut2str)) continue; // Ignore self-hit
				
				Double simPercent = Double.parseDouble(tokens[2]);
				int alignLen = Integer.parseInt(tokens[3]);
				int mismatches = Integer.parseInt(tokens[4]);
				int gapOpen = Integer.parseInt(tokens[5]);
				int ut1Start = Integer.parseInt(tokens[6]);
				int ut1End = Integer.parseInt(tokens[7]);
				int ut2Start = Integer.parseInt(tokens[8]);
				int ut2End = Integer.parseInt(tokens[9]);
				Double eVal = Double.parseDouble(tokens[10]);
				Float bitScore = Float.parseFloat(tokens[11]);
				
				int result = ut1str.compareTo(ut2str);
				if (result > 0) {
					String x = ut1str;
					ut1str = ut2str;
					ut2str = x;
					int t = ut1Start;
					ut1Start = ut2Start;
					ut2Start = t;
					t = ut1End;
					ut1End = ut2End;
					ut2End = t;
				}
				String key = ut1str + ":" + ut2str;
				if (pairs.contains(key)) continue;
				if (simPercent < similarity) {
					cntMinSim++;
					continue;
				}
			
				String idlen = utIdLen.get(ut1str);
				String tok[] = idlen.split(":");
				int id1 = Integer.parseInt(tok[0]);
				int len1 = Integer.parseInt(tok[1]);
				
				idlen = utIdLen.get(ut2str);
				tok = idlen.split(":");
				int id2 = Integer.parseInt(tok[0]);
				int len2 = Integer.parseInt(tok[1]);
				int polap1 = (int) (((double)alignLen/(double)len1) * 100.0);
				int polap2 = (int) (((double)alignLen/(double)len2) * 100.0);
				int pOlap = Math.min(polap1, polap2);

				if (pOlap < overlapCutoff) {
					cntMinOlap++;
					continue;
				}
				
				String sql = "INSERT into ztmp_hits SET " +
				"UTstr1 = " 		+ quote(ut1str) 	+ ", " +
				"UTid1  = " 		+ id1 			+ ", " +
				"UTlen1  = " 	+ len1 			+ ", " +
				"UTstr2 	= " 		+ quote(ut2str) + ", " +
				"UTid2	= " 		+ id2 			+ ", " +
				"UTlen2  = " 	+ len2 			+ ", " +
				"pOlp		= " + pOlap 			+ ", " +	
				"percentSim 	= " + simPercent 	+ ", " +
				"alignLen  	= " + alignLen		+ ", " +
				"utStart1 	= " + ut1Start		+ ", " +	
				"utEnd1		= " + ut1End			+ ", " +	
				"utStart2	= " + ut2Start		+ ", " +	
				"utEnd2	 	= " + ut2End			+ ", " +	
				"e_value 	= " + eVal 			+ ", " +
				"bit_score 	= " + bitScore 		+ ";";
				
				cmpDBC.executeUpdate(sql);
				cntLoad++;
				pairs.add(key);
				if (cntRead % 10000 == 0) 
					System.out.print("Load " + cntLoad + " hits    Read " + cntRead + " hits        \r");
			} // end while
			if (reader != null) reader.close();
			CompileMain.PrtSpMsg(1, "Load hits " + cntLoad);
			CompileMain.PrtSpMsg(1, "Under min overlap " + cntMinOlap);
			CompileMain.PrtSpMsg(1, "Under min simlarity " + cntMinSim);
			return true;	
		} 
		catch (Exception e) {
			String err = "Failed to enter blast hits into database.";
			CoreMain.PrtError(err);
			CoreMain.PrtError("strLine = '" + strLine + "'");
			ErrorReport.die(e, err);
			return false;
		} 
	}
	// orthoMCL has blast lines with something|id -- so to use the same blast file..
	 private String getID(String id) {
		Pattern u = Pattern.compile("(\\S+)\\|(\\S+)"); 
		Matcher m = u.matcher(id);
		if (m.find()) return m.group(2);
		else return id;
	}
	
	 private boolean createTmpTables() {
		try {
			String sqlU;
			sqlU = "CREATE TABLE ztmp_hits ( " +
				"zHTid 		bigint NOT NULL PRIMARY KEY AUTO_INCREMENT, " +
				// parents
				"UTstr1 		VARCHAR(30), " +
				"UTid1 		bigint, " +
				"UTlen1     int, " +
				"UTstr2 		VARCHAR(30), " +
				"UTid2		bigint, " +
				"UTlen2     int, " +
				"pOlp		smallint, " + // Max(olp1, olp2) 
				"percentSim 	double, " +  
				"alignLen 	smallint, " + 
				"utStart1 	smallint, " + 
				"utEnd1		smallint, " +
				"utStart2	smallint, " +
				"utEnd2	 	smallint, " +
				"e_value 	double, " +
				"bit_score 	float, " +
				"index idx1 (UTid1), " +
				"index idx2 (UTid2) " +
				");";
			cmpDBC.executeUpdate(sqlU);
			return true;
		}
		 catch(Exception e) {
			ErrorReport.die(e, "creating temporary schema");
		}		
		return false;
	}
	 private boolean removeTmpTables() {
		try {
			if (cmpDBC.hasTable("ztmp_hits")) cmpDBC.executeUpdate("DROP table ztmp_hits");
			return true;
		}
		 catch(Exception e) {
			ErrorReport.die(e, "removing temporary schema");			
		}
		return false;
	}
	/****************************************************************
	 * methods to compute functional POGs
	 */
	 private boolean findPOGs() {
		/* 
		 * get ztmp 1000 at a time sorted by alignAA
		 * if id2pog.containsKey(id1) and id2pog.containsKey(id2) continue
		 * int olap = rs.getInt("olp");
		 * int lenAA = rs.getInt
		 * 
		 * * the new id needs to be within the coordinates of the POGs
		 * ---------------
		 * ----------------------------
		 *               ..............  no
		 * ...............               yes
		 * if id2pog.containsKey(id1)
		 * 		POGs pog = id2pog.get(id1);
		 * 		if (good(pog.start1, rs.getInt(ut1id
		 * 	score(
		*/
	
		ResultSet rs;
		try {
			rs = cmpDBC.executeQuery("select UTstr1, UTstr2, alignLen, " +
					"utStart1, utEnd1, utStart2, utEnd2 from ztmp_hits order by alignLen");
			while (rs.next()) {
				
			}

			rs.close();
		}
		catch(Exception e) {
			ErrorReport.prtReport(e, "Creating maps and vectors of values");
		}
		return true;
	}

	 private class POGs {
		POGs(int id1, int id2, int o, int s, int l, int s1, int e1, int s2, int e2) {
			utid1=id1; utid2=id2;
			olap = o; sim = s; len = l;
			utstart1 = s1; utend1 = e1; utstart2=s2; utend2=e2;
			cntMem=1;
		}
		private void addMem(int id) {		
			cntMem++;
			idList.add(id);
		}
		int olap=0, sim=0, len=0;
		int utid1=0, utid2=0;
		int utstart1=0, utend1=0;
		int utstart2=0, utend2=0;

		int cntMem=0;
		Vector <Integer> idList = new Vector <Integer> ();
	}
	
	 private String quote(String word) {
		return "\"" + word + "\""; 
	}
	 private HashMap <Integer, Integer> id2pog = new HashMap <Integer, Integer> ();
	
	 private CompilePanel cmpPanel;
	 private DBConn cmpDBC;
	
	 private String prefix;
	 private double overlapCutoff, similarity;
	 private String blastFile;
}
